#include "zookeeperutil.h"
#include"mprpcapplication.h" //要连接zkserver，需要知道相应的地址
#include<iostream>
#include "logger.h"

//全局watch观察器，zkserver给zkclient的通知
void global_watcher(zhandle_t *zh, int type, int state, const char *path,void *watcherCtx){
    /*
     zh ：当前客户端的一个句柄，后面句柄绑定了一个信号量
    */
    if(type==ZOO_SESSION_EVENT) { //回调的消息类型时和会话相关的消息类型
        if(state==ZOO_CONNECTED_STATE) { //zkclient和zkserver连接成功
            sem_t* sem = (sem_t*)zoo_get_context(zh);
            sem_post(sem);
        }
    }
} 

ZkClient::ZkClient():m_zhandle(nullptr){

}

ZkClient::~ZkClient()
{
    if(m_zhandle!=nullptr){
        //已经和zkserver连接
        zookeeper_close(m_zhandle); //关闭句柄，释放资源
        m_zhandle = nullptr;
    }

}

void ZkClient::Start(){
    std::string host = MprpcApplication::GetInstance().GetConfig().Load("zookeeperip");
    std::string port = MprpcApplication::GetInstance().GetConfig().Load("zookeeperport");
    std::string connstr = host +":"+port;
    /*
    1.以固定格式组织的(ip:port)
    2.回调函数
    3.会话的超时时间 
    */

   /*
    zookeeper_mt:多线程版本
    zookeeper的API客户端程序提供了三个线程
    API调用线程，即执行zookeeper_init
    网络I/O线程，发起连接zookeeper_server
    当客户端接收了zkserver的响应时（连接成功），watcher回调线程（函数回调线程）
   */
   
   
    m_zhandle =  zookeeper_init(connstr.c_str(),global_watcher,30000,nullptr,nullptr,0); 
    if(nullptr==m_zhandle)  { //表示句柄的内存开辟到底是否成功
        LOG_ERR("zookeeper_init error! %s %s %d",__FILE__,__FUNCTION__,__LINE__);
        // std::cout<<"zookeeper_init error!"<<std::endl;
        exit(EXIT_FAILURE);
    }

    sem_t sem;
    sem_init(&sem,0,0);
    zoo_set_context(m_zhandle,&sem); //给文件句柄绑定信号量
    sem_wait(&sem); //m_zhandle收到服务端响应
    LOG_INFO("zookeeper_init_success!");
    // std::cout<<"zookeeper_init success"<<std::endl;
}


void ZkClient::Create(const char* path,const char* data,int datalen,int state){
    char path_buffer[128];
    int bufferlen = sizeof(path_buffer);
    int flag;
    //先判断path表示的znode节点是否存在，如果存在，就不再重复创建
    flag = zoo_exists(m_zhandle,path,0,nullptr);
    if(flag == ZNONODE){
        //不存在才创建，创建指定path的znode节点了
         flag = zoo_create(m_zhandle,path,data,datalen,&ZOO_OPEN_ACL_UNSAFE,state,path_buffer,bufferlen);
        if(flag==ZOK){
            LOG_INFO("znode create success... path:%s",path);
        }
        else{
            LOG_ERR("flag: %d %s %s %d",flag,__FILE__,__FUNCTION__,__LINE__);
            exit(EXIT_FAILURE);
        }  


    }
} 
//根据指定的path，获取znode的值 
std::string ZkClient::GetData(const char* path){
    char buffer[64];
    int bufferlen = sizeof(buffer);
    int flag = zoo_get(m_zhandle,path,0,buffer,&bufferlen,nullptr);
    if(flag==ZOK){
        return buffer;
    }
    else{
        LOG_ERR("get znode error... path: %s %s %s %d",path,__FILE__,__FUNCTION__,__LINE__);
        return "";
    }
}